﻿using System;
using System.Collections.Generic;
using System.Data.Services.Caching;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
namespace System.Data.Services.Providers
{
    /// <summary>Provides a reflection-based provider implementation.</summary>
    internal abstract class BaseServiceProvider : 
        IDataServiceMetadataProvider, 
        IDataServiceQueryProvider, 
        IDataServiceProviderBehavior, 
        IDisposable
    {
        /// <summary>Bindings Flags to be used for reflection.</summary>
        protected const BindingFlags ResourceContainerBindingFlags = BindingFlags.Instance | BindingFlags.Public;
        /// <summary>instance of the service to invoke service operations.</summary>
        private readonly object dataServiceInstance;
        /// <summary>Instance from which data is provided.</summary>
        private object dataSourceInstance;
        /// <summary>Metadata to be used by the service provider.</summary>
        private ProviderMetadataCacheItem metadata;
        /// <summary>Internal providers need to know if the metadata was loaded from cache or initialized.
        /// Once we make the providers completely public, we can get rid of this.</summary>
        private bool metadataRequiresInitialization;
        /// <summary>Returns the instance from which data is provided.</summary>
        public virtual object CurrentDataSource
        {
            [DebuggerStepThrough]
            get
            {
                return this.dataSourceInstance;
            }
            set
            {
                throw new NotSupportedException();
            }
        }
        /// <summary>Gets a value indicating whether null propagation is required in expression trees.</summary>
        public abstract bool IsNullPropagationRequired
        {
            get;
        }
        /// <summary>Namespace name for the EDM container.</summary>
        public abstract string ContainerNamespace
        {
            get;
        }
        /// <summary>Name of the EDM container</summary>
        public abstract string ContainerName
        {
            get;
        }
        /// <summary>Gets all available containers.</summary>
        /// <returns>An enumerable object with all available containers.</returns>
        public virtual IEnumerable<ResourceSet> ResourceSets
        {
            get
            {
                return this.metadata.EntitySets.Values;
            }
        }
        /// <summary>Returns all the types in this data source</summary>
        public virtual IEnumerable<ResourceType> Types
        {
            get
            {
                return
                    from c in this.metadata.ResourceTypeCacheItems
                    select c.ResourceType;
            }
        }
        /// <summary>Returns all known service operations.</summary>
        public virtual IEnumerable<ServiceOperation> ServiceOperations
        {
            get
            {
                foreach (ServiceOperation current in this.metadata.ServiceOperations.Values)
                {
                    yield return current;
                }
                yield break;
            }
        }
        /// <summary>
        /// Instance of provider behavior that defines the assumptions service should make
        /// about the provider.
        /// </summary>
        public abstract ProviderBehavior ProviderBehavior
        {
            get;
        }
        /// <summary>
        /// Gets the MetadataCacheItem containing all the cached metadata.
        /// </summary>
        protected ProviderMetadataCacheItem MetadataCacheItem
        {
            get
            {
                return this.metadata;
            }
        }
        /// <summary>Cache of immediate derived types per type.</summary>
        private Dictionary<ResourceType, List<ResourceType>> ChildTypesCache
        {
            [DebuggerStepThrough]
            get
            {
                return this.metadata.ChildTypesCache;
            }
        }
        /// <summary>Target type for the data provider </summary>
        private Type DataSourceType
        {
            [DebuggerStepThrough]
            get
            {
                return this.dataSourceInstance.GetType();
            }
        }
        /// <summary>
        /// Initializes a new System.Data.Services.BaseServiceProvider instance.
        /// </summary>
        /// <param name="dataServiceInstance">data service instance.</param>
        /// <param name="dataSourceInstance">data source instance.</param>
        protected BaseServiceProvider(object dataServiceInstance, object dataSourceInstance)
        {
            WebUtil.CheckArgumentNull<object>(dataServiceInstance, "dataServiceInstance");
            WebUtil.CheckArgumentNull<object>(dataSourceInstance, "dataSourceInstance");
            this.dataServiceInstance = dataServiceInstance;
            this.dataSourceInstance = dataSourceInstance;
        }
        /// <summary>Releases the current data source object as necessary.</summary>
        void IDisposable.Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }
        /// <summary>
        /// Returns the IQueryable that represents the container.
        /// </summary>
        /// <param name="container">resource set representing the entity set.</param>
        /// <returns>
        /// An IQueryable that represents the container; null if there is 
        /// no container for the specified name.
        /// </returns>
        public abstract IQueryable GetQueryRootForResourceSet(ResourceSet container);
        /// <summary>Gets the <see cref="T:System.Data.Services.Providers.ResourceType" /> for the specified <paramref name="resource" />.</summary>
        /// <param name="resource">Instance to extract a <see cref="T:System.Data.Services.Providers.ResourceType" /> from.</param>
        /// <returns>The <see cref="T:System.Data.Services.Providers.ResourceType" /> that describes this <paramref name="resource" /> in this provider.</returns>
        public virtual ResourceType GetResourceType(object resource)
        {
            WebUtil.CheckArgumentNull<object>(resource, "resource");
            return this.GetNonPrimitiveType(resource.GetType());
        }
        /// <summary>
        /// Get the value of the strongly typed property.
        /// </summary>
        /// <param name="target">instance of the type declaring the property.</param>
        /// <param name="resourceProperty">resource property describing the property.</param>
        /// <returns>value for the property.</returns>
        public virtual object GetPropertyValue(object target, ResourceProperty resourceProperty)
        {
            WebUtil.CheckArgumentNull<object>(target, "target");
            WebUtil.CheckArgumentNull<ResourceProperty>(resourceProperty, "resourceProperty");
            object result;
            try
            {
                ResourceTypeCacheItem resourceTypeCacheItem = this.ResolveNonPrimitiveTypeCacheItem(target.GetType());
                PropertyInfo propertyInfo = this.GetResourcePropertyCacheItem(resourceTypeCacheItem, resourceProperty).PropertyInfo;
                result = propertyInfo.GetGetMethod().Invoke(target, null);
            }
            catch (TargetInvocationException exception)
            {
                ErrorHandler.HandleTargetInvocationException(exception);
                throw;
            }
            return result;
        }
        /// <summary>
        /// Gets the value of the open property.
        /// </summary>
        /// <param name="target">instance of the resource type.</param>
        /// <param name="propertyName">name of the property.</param>
        /// <returns>the value of the open property. If the property is not present, return null.</returns>
        public abstract object GetOpenPropertyValue(object target, string propertyName);
        /// <summary>
        /// Get the name and values of all the properties defined in the given instance of an open type.
        /// </summary>
        /// <param name="target">instance of a open type.</param>
        /// <returns>collection of name and values of all the open properties.</returns>
        public abstract IEnumerable<KeyValuePair<string, object>> GetOpenPropertyValues(object target);
        /// <summary>
        /// Invoke the given service operation instance.
        /// </summary>
        /// <param name="serviceOperation">metadata for the service operation to invoke.</param>
        /// <param name="parameters">list of parameters to pass to the service operation.</param>
        /// <returns>returns the result by the service operation instance.</returns>
        public virtual object InvokeServiceOperation(ServiceOperation serviceOperation, object[] parameters)
        {
            WebUtil.CheckArgumentNull<ServiceOperation>(serviceOperation, "serviceOperation");
            object result;
            try
            {
                result = ((MethodInfo)serviceOperation.CustomState).Invoke(this.dataServiceInstance, BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy, null, parameters, CultureInfo.InvariantCulture);
            }
            catch (TargetInvocationException exception)
            {
                ErrorHandler.HandleTargetInvocationException(exception);
                throw;
            }
            return result;
        }
        /// <summary>
        /// The method must return a collection of all the types derived from <paramref name="resourceType" />.
        /// The collection returned should NOT include the type passed in as a parameter.
        /// An implementer of the interface should return null if the type does not have any derived types (ie. null == no derived types).
        /// </summary>
        /// <param name="resourceType">Resource to get derived resource types from.</param>
        /// <returns>
        /// A collection of resource types (<see cref="T:System.Data.Services.Providers.ResourceType" />) derived from the specified <paramref name="resourceType" /> 
        /// or null if there no types derived from the specified <paramref name="resourceType" /> exist.
        /// </returns>
        public virtual IEnumerable<ResourceType> GetDerivedTypes(ResourceType resourceType)
        {
            WebUtil.CheckArgumentNull<ResourceType>(resourceType, "resourceType");
            if (!this.ChildTypesCache.ContainsKey(resourceType))
            {
                throw new InvalidOperationException(Strings.BaseServiceProvider_UnknownResourceTypeInstance(resourceType.FullName));
            }
            List<ResourceType> list = this.ChildTypesCache[resourceType];
            if (list != null)
            {
                foreach (ResourceType current in list)
                {
                    yield return current;
                    foreach (ResourceType current2 in this.GetDerivedTypes(current))
                    {
                        yield return current2;
                    }
                }
            }
            yield break;
        }
        /// <summary>
        /// Returns true if <paramref name="resourceType" /> represents an Entity Type which has derived Entity Types, else false.
        /// </summary>
        /// <param name="resourceType">instance of the resource type in question.</param>
        /// <returns>True if <paramref name="resourceType" /> represents an Entity Type which has derived Entity Types, else false.</returns>
        public virtual bool HasDerivedTypes(ResourceType resourceType)
        {
            WebUtil.CheckArgumentNull<ResourceType>(resourceType, "resourceType");
            if (!this.ChildTypesCache.ContainsKey(resourceType))
            {
                throw new InvalidOperationException(Strings.BaseServiceProvider_UnknownResourceTypeInstance(resourceType.FullName));
            }
            return this.ChildTypesCache[resourceType] != null;
        }
        /// <summary>Given the specified name, tries to find a resource set.</summary>
        /// <param name="name">Name of the resource set to resolve.</param>
        /// <param name="resourceSet">Returns the resolved resource set, null if no resource set for the given name was found.</param>
        /// <returns>True if resource set with the given name was found, false otherwise.</returns>
        public virtual bool TryResolveResourceSet(string name, out ResourceSet resourceSet)
        {
            WebUtil.CheckStringArgumentNullOrEmpty(name, "name");
            return this.metadata.EntitySets.TryGetValue(name, out resourceSet);
        }
        /// <summary>Given the specified name, tries to find a service operation.</summary>
        /// <param name="name">Name of the service operation to resolve.</param>
        /// <param name="serviceOperation">Returns the resolved service operation, null if no service operation was found for the given name.</param>
        /// <returns>True if we found the service operation for the given name, false otherwise.</returns>
        public virtual bool TryResolveServiceOperation(string name, out ServiceOperation serviceOperation)
        {
            WebUtil.CheckStringArgumentNullOrEmpty(name, "name");
            return this.metadata.ServiceOperations.TryGetValue(name, out serviceOperation);
        }
        /// <summary>Given the specified name, tries to find a type.</summary>
        /// <param name="name">Name of the type to resolve.</param>
        /// <param name="resourceType">Returns the resolved resource type, null if no resource type for the given name was found.</param>
        /// <returns>True if we found the resource type for the given name, false otherwise.</returns>
        public virtual bool TryResolveResourceType(string name, out ResourceType resourceType)
        {
            WebUtil.CheckStringArgumentNullOrEmpty(name, "name");
            foreach (ResourceTypeCacheItem current in this.metadata.ResourceTypeCacheItems)
            {
                if (current.ResourceType.FullName == name)
                {
                    resourceType = current.ResourceType;
                    return true;
                }
            }
            resourceType = null;
            return false;
        }
        /// <summary>
        /// Gets the ResourceAssociationSet instance when given the source association end.
        /// </summary>
        /// <param name="resourceSet">Resource set of the source association end.</param>
        /// <param name="resourceType">Resource type of the source association end.</param>
        /// <param name="resourceProperty">Resource property of the source association end.</param>
        /// <returns>ResourceAssociationSet instance.</returns>
        public abstract ResourceAssociationSet GetResourceAssociationSet(ResourceSet resourceSet, ResourceType resourceType, ResourceProperty resourceProperty);
        /// <summary>
        /// Called by the service to let the provider perform data model validation.
        /// </summary>
        /// <param name="knownTypes">Collection of known types.</param>
        /// <param name="useMetadataCacheOrder">Whether to use metadata cache ordering instead of default ordering.</param>
        public virtual void FinalizeMetadataModel(IEnumerable<Type> knownTypes, bool useMetadataCacheOrder)
        {
            if (this.metadataRequiresInitialization)
            {
                this.PopulateMetadataForUserSpecifiedTypes(knownTypes, this.metadata);
                if (useMetadataCacheOrder)
                {
                    foreach (ResourceSet current in this.metadata.EntitySets.Values)
                    {
                        current.UseMetadataKeyOrder = true;
                    }
                }
                this.CheckModelConsistency();
                this.MakeMetadataReadonly();
            }
        }
        /// <summary>
        /// Return the list of custom annotation for the entity container with the given name.
        /// </summary>
        /// <param name="entityContainerName">Name of the EntityContainer.</param>
        /// <returns>Return the list of custom annotation for the entity container with the given name.</returns>
        public virtual IEnumerable<KeyValuePair<string, object>> GetEntityContainerAnnotations(string entityContainerName)
        {
            return WebUtil.EmptyKeyValuePairStringObject;
        }
        /// <summary>
        /// Returns the "T" in the IQueryable of T implementation of type.
        /// </summary>
        /// <param name="type">Type to check.</param>
        /// <param name="typeFilter">filter against which the type is checked</param>
        /// <returns>
        /// The element type for the generic IQueryable interface of the type,
        /// or null if it has none or if it's ambiguous.
        /// </returns>
        internal static Type GetGenericInterfaceElementType(Type type, TypeFilter typeFilter)
        {
            if (typeFilter(type, null))
            {
                return type.GetGenericArguments()[0];
            }
            Type[] array = type.FindInterfaces(typeFilter, null);
            if (array == null || array.Length != 1)
            {
                return null;
            }
            return array[0].GetGenericArguments()[0];
        }
        /// <summary>
        /// Returns the type of the IQueryable if the type implements IQueryable interface
        /// </summary>
        /// <param name="type">clr type on which IQueryable check needs to be performed.</param>
        /// <returns>Element type if the property type implements IQueryable, else returns null</returns>
        internal static Type GetIQueryableElement(Type type)
        {
            return BaseServiceProvider.GetGenericInterfaceElementType(type, new TypeFilter(BaseServiceProvider.IQueryableTypeFilter));
        }
        /// <summary>
        /// Returns the type of the IEnumerable if the type implements IEnumerable interface; null otherwise.
        /// </summary>
        /// <param name="type">type that needs to be checked</param>
        /// <returns>Element type if the type implements IEnumerable, else returns null</returns>
        internal static Type GetIEnumerableElement(Type type)
        {
            return BaseServiceProvider.GetGenericInterfaceElementType(type, new TypeFilter(BaseServiceProvider.IEnumerableTypeFilter));
        }
        /// <summary>Checks whether the specified method has a SingleResultAttribute declared on it.</summary>
        /// <param name="method">Method to check.</param>
        /// <returns>
        /// true if the specified method (in its declared type or in an 
        /// ancestor declaring the type) has the SingleResultAttribute set.
        /// </returns>
        internal static bool MethodHasSingleResult(MethodInfo method)
        {
            return method.GetCustomAttributes(typeof(SingleResultAttribute), true).Length > 0;
        }
        /// <summary>
        /// Gets the MIME type declared on the specified <paramref name="member" />.
        /// </summary>
        /// <param name="member">Member to check.</param>
        /// <returns>
        /// The MIME type declared on the specified <paramref name="member" />; null
        /// if no attribute is declared.
        /// </returns>
        internal static MimeTypeAttribute GetMimeTypeAttribute(MemberInfo member)
        {
            return member.ReflectedType.GetCustomAttributes(typeof(MimeTypeAttribute), true).Cast<MimeTypeAttribute>().FirstOrDefault((MimeTypeAttribute o) => o.MemberName == member.Name);
        }
        /// <summary>
        /// Looks up the metadata in the cache. If not present in the cache, then loads metadata from the provider.
        /// </summary>
        /// <param name="skipServiceOperations">Should service operations be loaded.</param>
        internal void LoadMetadata(bool skipServiceOperations)
        {
            Type type = this.dataServiceInstance.GetType();
            Type type2 = this.dataSourceInstance.GetType();
            this.metadata = MetadataCache<ProviderMetadataCacheItem>.TryLookup(type, this.dataSourceInstance);
            if (this.metadata == null)
            {
                this.metadata = new ProviderMetadataCacheItem(type2);
                this.PopulateMetadata(this.metadata);
                if (!skipServiceOperations)
                {
                    this.LoadServiceOperations();
                }
                this.metadataRequiresInitialization = true;
            }
        }
        /// <summary>
        /// Find the corresponding ResourceType for a given Type, primitive or not
        /// </summary>
        /// <param name="metadataCacheItem">Instance of ProviderMetadataCacheItem.</param>
        /// <param name="type">Type to look for</param>
        /// <param name="resourceType">Corresponding ResourceType, if found</param>
        /// <returns>True if type found, false otherwise</returns>
        protected static bool TryGetType(ProviderMetadataCacheItem metadataCacheItem, Type type, out ResourceType resourceType)
        {
            resourceType = PrimitiveResourceTypeMap.TypeMap.GetPrimitive(type);
            if (resourceType == null)
            {
                resourceType = metadataCacheItem.TryGetResourceType(type);
            }
            return resourceType != null;
        }
        /// <summary>
        /// Get the PropertyInfo for the given resource property
        /// </summary>
        /// <param name="resourceTypeCacheItem">Instance of ResourceTypeCacheItem containing the ResourceType instance.</param>
        /// <param name="resourceProperty">ResourceProperty instance.</param>
        /// <returns>PropertyInfo instance for the given ResourceProperty.</returns>
        protected ResourcePropertyCacheItem GetResourcePropertyCacheItem(ResourceTypeCacheItem resourceTypeCacheItem, ResourceProperty resourceProperty)
        {
            ResourceType declaringTypeForProperty = resourceTypeCacheItem.ResourceType.GetDeclaringTypeForProperty(resourceProperty, null);
            if (declaringTypeForProperty != resourceTypeCacheItem.ResourceType)
            {
                resourceTypeCacheItem = this.ResolveNonPrimitiveTypeCacheItem(declaringTypeForProperty.InstanceType);
            }
            return resourceTypeCacheItem.GetResourcePropertyCacheItem(resourceProperty);
        }
        /// <summary>Checks that the metadata model is consistent.</summary>
        protected virtual void CheckModelConsistency()
        {
        }
        /// <summary>Releases the current data source object as necessary.</summary>
        /// <param name="disposing">
        /// Whether this method is called from an explicit call to Dispose by 
        /// the consumer, rather than during finalization.
        /// </param>
        protected virtual void Dispose(bool disposing)
        {
            WebUtil.Dispose(this.dataSourceInstance);
            this.dataSourceInstance = null;
        }
        /// <summary>
        /// Populates the metadata for the given provider
        /// </summary>
        /// <param name="metadataCacheItem">Instance of ProviderMetadataCacheItem in which metadata needs to be populated.</param>
        protected abstract void PopulateMetadata(ProviderMetadataCacheItem metadataCacheItem);
        /// <summary>
        /// Populate types for metadata specified by the provider
        /// </summary>
        /// <param name="userSpecifiedTypes">list of types specified by the provider</param>
        /// <param name="metadataCacheItem">Instance of ProviderMetadataCacheItem.</param>
        protected abstract void PopulateMetadataForUserSpecifiedTypes(IEnumerable<Type> userSpecifiedTypes, ProviderMetadataCacheItem metadataCacheItem);
        /// <summary>
        /// Populate metadata for the given clr type.
        /// </summary>
        /// <param name="type">type whose metadata needs to be loaded.</param>
        /// <param name="metadataCacheItem">Instance of ProviderMetadataCacheItem.</param>
        /// <returns>resource type containing metadata for the given clr type.</returns>
        protected abstract ResourceType PopulateMetadataForType(Type type, ProviderMetadataCacheItem metadataCacheItem);
        /// <summary>
        /// Returns the resource type for the corresponding clr type.
        /// </summary>
        /// <param name="type">clrType whose corresponding resource type needs to be returned</param>
        /// <returns>Returns the resource type</returns>
        protected virtual ResourceTypeCacheItem ResolveNonPrimitiveTypeCacheItem(Type type)
        {
            return this.metadata.TryGetResourceTypeCacheItem(type);
        }
        /// <summary>
        /// Returns the resource type for the corresponding clr type.
        /// </summary>
        /// <param name="type">clrType whose corresponding resource type needs to be returned</param>
        /// <returns>Returns the resource type</returns>
        protected ResourceType ResolveNonPrimitiveType(Type type)
        {
            ResourceTypeCacheItem resourceTypeCacheItem = this.ResolveNonPrimitiveTypeCacheItem(type);
            if (resourceTypeCacheItem == null)
            {
                return null;
            }
            return resourceTypeCacheItem.ResourceType;
        }
        /// <summary>
        /// Get the QueryRoot delegate for the given ResourceSet.
        /// </summary>
        /// <param name="resourceSet">ResourceSet instance.</param>
        /// <returns>the delegate for the given ResourceSet.</returns>
        protected Func<object, IQueryable> GetQueryRootDelegate(ResourceSet resourceSet)
        {
            Func<object, IQueryable> result;
            this.metadata.QueryRootCache.TryGetValue(resourceSet, out result);
            return result;
        }
        /// <summary>Filter callback for finding IQueryable implementations.</summary>
        /// <param name="m">Type to inspect.</param>
        /// <param name="filterCriteria">Filter criteria.</param>
        /// <returns>true if the specified type is an IQueryable of T; false otherwise.</returns>
        private static bool IQueryableTypeFilter(Type m, object filterCriteria)
        {
            return m.IsGenericType && m.GetGenericTypeDefinition() == typeof(IQueryable<>);
        }
        /// <summary>Filter callback for finding IEnumerable implementations.</summary>
        /// <param name="m">Type to inspect.</param>
        /// <param name="filterCriteria">Filter criteria.</param>
        /// <returns>true if the specified type is an IEnumerable of T; false otherwise.</returns>
        private static bool IEnumerableTypeFilter(Type m, object filterCriteria)
        {
            return m.IsGenericType && m.GetGenericTypeDefinition() == typeof(IEnumerable<>);
        }
        /// <summary>Adds service operations based on methods on the data service type.</summary>
        private void LoadServiceOperations()
        {
            Type type = this.dataServiceInstance.GetType();
            ServiceOperationProvider serviceOperationProvider = new ServiceOperationProvider(type, new Func<Type, ResourceType>(this.ResolveResourceType), new Func<ResourceType, MethodInfo, ResourceSet>(this.ResolveResourceSet));
            foreach (ServiceOperation current in serviceOperationProvider.ServiceOperations)
            {
                if (this.metadata.ServiceOperations.ContainsKey(current.Name))
                {
                    throw new InvalidOperationException(Strings.BaseServiceProvider_OverloadingNotSupported(type, (MethodInfo)current.CustomState));
                }
                this.metadata.ServiceOperations.Add(current.Name, current);
            }
        }
        /// <summary>
        /// Given a CLR type, provides the corresponding <see cref="T:System.Data.Services.Providers.ResourceType" /> by either looking it up, or loading it's metadata.
        /// </summary>
        /// <param name="type">CLR type for which resource type is being looked up.</param>
        /// <returns><see cref="T:System.Data.Services.Providers.ResourceType" /> corresponding to <paramref name="type" />.</returns>
        private ResourceType ResolveResourceType(Type type)
        {
            return this.PopulateMetadataForType(type, this.metadata);
        }
        /// <summary>
        /// Given a <see cref="T:System.Data.Services.Providers.ResourceType" />, finds the corresponding <see cref="T:System.Data.Services.Providers.ResourceSet" />.
        /// </summary>
        /// <param name="resourceType">Given resource type.</param>
        /// <param name="method">Method implementing service operation.</param>
        /// <returns><see cref="T:System.Data.Services.Providers.ResourceSet" /> corresponding to <paramref name="resourceType" />.</returns>
        private ResourceSet ResolveResourceSet(ResourceType resourceType, MethodInfo method)
        {
            ResourceSet result;
            this.TryFindAnyContainerForType(resourceType, out result);
            return result;
        }
        /// <summary>Make all the metadata readonly</summary>
        private void MakeMetadataReadonly()
        {
            foreach (ResourceSet current in this.ResourceSets)
            {
                current.SetReadOnly();
            }
            foreach (ResourceType current2 in this.Types)
            {
                current2.SetReadOnly();
                current2.PropertiesDeclaredOnThisType.Count<ResourceProperty>();
            }
            foreach (ServiceOperation current3 in this.ServiceOperations)
            {
                current3.SetReadOnly();
            }
            this.metadata = MetadataCache<ProviderMetadataCacheItem>.AddCacheItem(this.dataServiceInstance.GetType(), this.dataSourceInstance, this.metadata);
        }
        /// <summary>
        /// Returns the resource type for the corresponding clr type.
        /// If the given clr type is a collection, then resource type describes the element type of the collection.
        /// </summary>
        /// <param name="type">clrType whose corresponding resource type needs to be returned</param>
        /// <returns>Returns the resource type</returns>
        private ResourceType GetNonPrimitiveType(Type type)
        {
            ResourceType resourceType = this.ResolveNonPrimitiveType(type);
            if (resourceType == null)
            {
                Type iEnumerableElement = BaseServiceProvider.GetIEnumerableElement(type);
                if (iEnumerableElement != null)
                {
                    resourceType = PrimitiveResourceTypeMap.TypeMap.GetPrimitive(iEnumerableElement);
                    if (resourceType == null)
                    {
                        resourceType = this.ResolveNonPrimitiveType(iEnumerableElement);
                    }
                }
            }
            return resourceType;
        }
        /// <summary>
        /// Looks for the first resource set that the specified <paramref name="type" />
        /// could belong to.
        /// </summary>
        /// <param name="type">Type to look for.</param>
        /// <param name="container">After the method returns, the container to which the type could belong.</param>
        /// <returns>true if a container was found; false otherwise.</returns>
        private bool TryFindAnyContainerForType(ResourceType type, out ResourceSet container)
        {
            foreach (ResourceSet current in this.metadata.EntitySets.Values)
            {
                if (current.ResourceType.IsAssignableFrom(type))
                {
                    container = current;
                    return true;
                }
            }
            container = null;
            return false;
        }
    }
}
